import numpy
import png2obj.image
import png2obj.imagedata
import png2obj.mesh
import png2obj.texture_style
from scipy.spatial import Delaunay

def makeContour(data):
    # given bordered binary data, produces vertices
    
    directions = numpy.array([
        [0, 1],  # S
        [-1, 0], # W
        [0, -1], # N
        [1, 0],  # E
        ])

    leftBorders = numpy.array([
        [0, 0], # S
        [-1, 0],  # W
        [-1, -1], # N
        [0, -1],# E
        ])

    rightBorders = numpy.roll(leftBorders, -1, axis=0)
    
    nx, ny = data.shape
    
    # find starting point
    t = numpy.transpose(numpy.nonzero(data))
    startPos = t[0]
    result = numpy.array([startPos]).T

    # walk
    currDirection = 0
    currPos = startPos + directions[currDirection]
    
    while any(startPos != currPos):
        # debug
        result = numpy.concatenate((result, numpy.array([currPos]).T), axis=1)
        
        # turn right?
        rightDirection = (currDirection + 1) % 4
        leftPosition = tuple(leftBorders[rightDirection] + currPos)
        rightPosition = tuple(rightBorders[rightDirection] + currPos)
        if data[leftPosition] != data[rightPosition]:
            currDirection = rightDirection
            currPos += directions[currDirection]
            continue

        # continue forwards?
        leftPosition = tuple(leftBorders[currDirection] + currPos)
        rightPosition = tuple(rightBorders[currDirection] + currPos)
        if data[leftPosition] != data[rightPosition]:
            currPos += directions[currDirection]
            continue

        # otherwise, turn left
        currDirection = (currDirection - 1) % 4
        currPos += directions[currDirection]
        
    return result
    
def removeInnerCorners(contour, maxArea = 1):
    _, nVertices = contour.shape
    select = numpy.ones((nVertices), bool)
    for i in range(nVertices):
        vPrev = contour[:, (i - 1) % nVertices]
        vCurr = contour[:, i]
        vNext = contour[:, (i + 1) % nVertices]
        dPrev = vCurr - vPrev
        dNext = vNext - vCurr
        area = dPrev[1] * dNext[0] - dPrev[0] * dNext[1]
        if area >= 0 and area <= maxArea:
            select[i] = False
    contour = contour[:, select]
    return contour
    
def removeFlatSegments(contour):
    select = numpy.zeros_like(contour)

def coneStyle(name, slots, levels, removeInnerCornerArea = 1, placeholderScale = 4, textureStyle=png2obj.texture_style.ZProjectionStyle):
    data = addBorder(slots)
    data = (data > 0)
    textureStyle = textureStyle(data)
    
    nx, ny = data.shape
    mesh = png2obj.mesh.Mesh(name)
    
    contour = makeContour(data)
    contour = removeInnerCorners(contour, removeInnerCornerArea)
    
    xs, ys = png2obj.mesh.indexToMeshCoord(contour[0], contour[1], nx, ny)
    nVertices = len(xs)
    
    for iLevel in range(len(levels) - 1):
        loElevation, loScale = levels[iLevel]
        hiElevation, hiScale = levels[iLevel+1]
        for iVertex in range(nVertices):
            negX = xs[iVertex]
            negY = ys[iVertex]
            posX = xs[(iVertex+1) % nVertices]
            posY = ys[(iVertex+1) % nVertices]
            face = []
            if loScale != 0.0:
                face.append([negX*loScale, negY*loScale, -loElevation])
                face.append([posX*loScale, posY*loScale, -loElevation])
            else:
                face.append([0.0, 0.0, -loElevation])
            if hiScale != 0.0:
                face.append([posX*hiScale, posY*hiScale, -hiElevation])
                face.append([negX*hiScale, negY*hiScale, -hiElevation])
            else:
                face.append([0.0, 0.0, -hiElevation])
            negFace = numpy.transpose(face)
            posFace = negateVertices(negFace)
            mesh.addFace(posFace, textureStyle.texCoords(posFace, 2))
            mesh.addFace(negFace, textureStyle.texCoords(negFace, 2))
    
    return mesh, textureStyle.placeholderImageData(placeholderScale)
            
    
def addBorder(data, width=1):
    nx, ny = data.shape
    result = numpy.zeros((nx+2*width, ny+2*width), float)
    result[width:-width, width:-width] = data
    return result

def negateVertices(vertices):
    result = numpy.copy(vertices[:, ::-1])
    result[2, :] = -result[2, :]
    return result